package util.image;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import org.apache.http.util.ExceptionUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.AttributedString;

/**
 * image 工具类
 * @Author zt
 * @date 2021/11/11 15:43
 * @Version 1.0
 */
public class ImageUtil {


    public static final float DEFAULT_QUALITY = 0.2125f ;


    /**
     *
     * 添加图片水印操作(物理存盘,使用默认格式)
     *
     * @param imgPath 待处理图片
     * @param markPath  水印图片
     * @param x 水印位于图片左上角的 x 坐标值
     * @param y 水印位于图片左上角的 y 坐标值
     * @param alpha 水印透明度 0.1f ~ 1.0f
     * @param destPath 文件存放路径
     * @throws Exception 异常
     *
     */
    public static void addWaterMark(String imgPath, String markPath, int x, int y, float alpha,String destPath) throws Exception{
        try {
            BufferedImage bufferedImage = addWaterMark(imgPath, markPath, x, y, alpha);
            ImageIO.write(bufferedImage, imageFormat(imgPath), new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("添加图片水印异常");
        }
    }


    /**
     *
     * 添加图片水印操作(物理存盘,自定义格式)
     *
     * @param imgPath 待处理图片
     * @param markPath 水印图片
     * @param x 水印位于图片左上角的 x 坐标值
     * @param y 水印位于图片左上角的 y 坐标值
     * @param alpha 水印透明度 0.1f ~ 1.0f
     * @param format 添加水印后存储的格式
     * @param destPath 文件存放路径
     * @throws Exception 异常
     *
     */
    public static void addWaterMark(String imgPath, String markPath, int x, int y, float alpha,String format,String destPath) throws Exception{
        try {
            BufferedImage bufferedImage = addWaterMark(imgPath, markPath, x, y, alpha);
            ImageIO.write(bufferedImage,format , new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("添加图片水印异常");
        }
    }


    /**
     *
     * 添加图片水印操作,返回BufferedImage对象
     *
     * @param imgPath 待处理图片
     * @param markPath 水印图片
     * @param x 水印位于图片左上角的 x 坐标值
     * @param y  水印位于图片左上角的 y 坐标值
     * @param alpha   水印透明度 0.1f ~ 1.0f
     * @return  处理后的图片对象
     * @throws Exception 异常
     *
     */
    public static BufferedImage addWaterMark(String imgPath, String markPath, int x, int y, float alpha) throws Exception{
        BufferedImage targetImage = null;
        try {
            // 加载待处理图片文件
            Image img = ImageIO.read(new File(imgPath));

            //创建目标图象文件
            targetImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
            Graphics2D g = targetImage.createGraphics();
            g.drawImage(img, 0, 0, null);

            // 加载水印图片文件
            Image markImg = ImageIO.read(new File(markPath));
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
            g.drawImage(markImg, x, y, null);
            g.dispose();
        } catch (Exception e) {
            throw new RuntimeException("添加图片水印操作异常");
        }
        return targetImage;

    }

    /**
     *
     * 添加文字水印操作(物理存盘,使用默认格式)
     *
     * @param imgPath 待处理图片
     * @param text  水印文字
     * @param font 水印字体信息    不写默认值为宋体
     * @param color 水印字体颜色
     * @param x 水印位于图片左上角的 x 坐标值
     * @param y 水印位于图片左上角的 y 坐标值
     * @param alpha  水印透明度 0.1f ~ 1.0f 添加水印后存储的格式
     * @param destPath 文件存放路径
     * @throws Exception 异常
     */
    public static void addTextMark(String imgPath, String text, Font font, Color color, float x, float y, float alpha,String destPath) throws Exception{
        try {
            BufferedImage bufferedImage = addTextMark(imgPath, text, font, color, x, y, alpha);
            ImageIO.write(bufferedImage, imageFormat(imgPath), new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("图片添加文字水印异常");
        }
    }

    /**
     *
     * 添加文字水印操作(物理存盘,自定义格式)
     *
     * @param imgPath 待处理图片
     * @param text 水印文字
     * @param font 水印字体信息    不写默认值为宋体
     * @param color 水印字体颜色
     * @param x 水印位于图片左上角的 x 坐标值
     * @param y  水印位于图片左上角的 y 坐标值
     * @param alpha 水印透明度 0.1f ~ 1.0f
     * @param format 添加水印后存储的格式
     * @param destPath 文件存放路径
     * @throws Exception 异常
     */
    public static void addTextMark(String imgPath, String text, Font font, Color color, float x, float y, float alpha,String format,String destPath) throws Exception{
        try {
            BufferedImage bufferedImage = addTextMark(imgPath, text, font, color, x, y, alpha);
            ImageIO.write(bufferedImage, format, new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("图片添加文字水印异常");
        }
    }

    /**
     *
     * 添加文字水印操作,返回BufferedImage对象
     *
     * @param imgPath 待处理图片
     * @param text 水印文字
     * @param font  水印字体信息    不写默认值为宋体
     * @param color 水印字体颜色
     * @param x 水印位于图片左上角的 x 坐标值
     * @param y 水印位于图片左上角的 y 坐标值
     * @param alpha 水印透明度 0.1f ~ 1.0f
     * @return 处理后的图片对象
     * @throws Exception 异常
     */

    public static BufferedImage addTextMark(String imgPath, String text, Font font, Color color, float x, float y, float alpha) throws Exception{
        BufferedImage targetImage = null;
        try {
            Font Dfont = (font == null) ? new Font("宋体", 20, 13) : font;
            Image img = ImageIO.read(new File(imgPath));
            //创建目标图像文件
            targetImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
            Graphics2D g = targetImage.createGraphics();
            g.drawImage(img, 0, 0, null);
            g.setColor(color);
            g.setFont(Dfont);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
            g.drawString(text, x, y);
            g.dispose();
        } catch (Exception e) {
            throw new RuntimeException("添加文字水印操作异常");
        }
        return targetImage;
    }



    /**
     *
     *
     *
     * 压缩图片操作(文件物理存盘,使用默认格式)
     *
     * @param imgPath 待处理图片
     * @param quality  图片质量(0-1之間的float值)
     * @param width  输出图片的宽度    输入负数参数表示用原来图片宽
     * @param height 输出图片的高度    输入负数参数表示用原来图片高
     * @param autoSize   是否等比缩放 true表示进行等比缩放 false表示不进行等比缩放
     * @param destPath 文件存放路径
     * @throws Exception 异常
     */
    public static void compressImage(String imgPath,float quality,int width, int height, boolean autoSize,String destPath)throws Exception{
        try {
            BufferedImage bufferedImage = compressImage(imgPath, quality, width, height, autoSize);
            ImageIO.write(bufferedImage, imageFormat(imgPath), new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("图片压缩异常");
        }

    }


    /**
     *
     * 压缩图片操作(文件物理存盘,可自定义格式)
     *
     * @param imgPath 待处理图片
     * @param quality 图片质量(0-1之間的float值)
     * @param width  输出图片的宽度    输入负数参数表示用原来图片宽
     * @param height  输出图片的高度    输入负数参数表示用原来图片高
     * @param autoSize 是否等比缩放 true表示进行等比缩放 false表示不进行等比缩放
     * @param format 压缩后存储的格式
     * @param destPath 文件存放路径
     *
     * @throws Exception 异常
     */
    public static void compressImage(String imgPath,float quality,int width, int height, boolean autoSize,String format,String destPath)throws Exception{
        try {
            BufferedImage bufferedImage = compressImage(imgPath, quality, width, height, autoSize);
            ImageIO.write(bufferedImage, format, new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("图片压缩异常");
        }
    }


    /**
     *
     * 压缩图片操作,返回BufferedImage对象
     *
     * @param imgPath 待处理图片
     * @param quality 图片质量(0-1之間的float值)
     * @param width  输出图片的宽度    输入负数参数表示用原来图片宽
     * @param height  输出图片的高度    输入负数参数表示用原来图片高
     *
     * @param autoSize 是否等比缩放 true表示进行等比缩放 false表示不进行等比缩放
     * @return 处理后的图片对象
     * @throws Exception 异常
     */
    public static BufferedImage compressImage(String imgPath,float quality,int width, int height, boolean autoSize)throws Exception{
        BufferedImage targetImage = null;
        if(quality<0F||quality>1F){
            quality = DEFAULT_QUALITY;
        }
        try {
            Image img = ImageIO.read(new File(imgPath));
            //如果用户输入的图片参数合法则按用户定义的复制,负值参数表示执行默认值
            int newwidth =( width > 0 ) ? width : img.getWidth(null);
            //如果用户输入的图片参数合法则按用户定义的复制,负值参数表示执行默认值
            int newheight = ( height > 0 )? height: img.getHeight(null);
            //如果是自适应大小则进行比例缩放
            if(autoSize){
                // 为等比缩放计算输出的图片宽度及高度
                double Widthrate = ((double) img.getWidth(null)) / (double) width + 0.1;
                double heightrate = ((double) img.getHeight(null))/ (double) height + 0.1;
                double rate = Widthrate > heightrate ? Widthrate : heightrate;
                newwidth = (int) (((double) img.getWidth(null)) / rate);
                newheight = (int) (((double) img.getHeight(null)) / rate);
            }
            //创建目标图像文件
            targetImage = new BufferedImage(newwidth,newheight,BufferedImage.TYPE_INT_RGB);
            Graphics2D g = targetImage.createGraphics();
            g.drawImage(img, 0, 0, newwidth, newheight, null);
            //如果添加水印或者文字则继续下面操作,不添加的话直接返回目标文件----------------------
            g.dispose();

        } catch (Exception e) {
            throw new RuntimeException("图片压缩操作异常");
        }
        return targetImage;
    }



    /**
     * 图片黑白化操作(文件物理存盘,使用默认格式)
     *
     * @param imgPath 处理的图片对象
     * @param destPath 目标文件地址
     * @throws Exception 异常
     *
     */
    public static void imageGray(String imgPath, String destPath)throws Exception{
        imageGray(imgPath, imageFormat(imgPath), destPath);
    }


    /**
     * 图片黑白化操作(文件物理存盘,可自定义格式)
     * @param imgPath 处理的图片对象
     * @param format  图片格式
     * @param destPath 目标文件地址
     * @throws Exception 异常
     */
    public static void imageGray(String imgPath,String format, String destPath)throws Exception{
        try {
            BufferedImage bufferedImage = ImageIO.read(new File(imgPath));
            ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
            ColorConvertOp op = new ColorConvertOp(cs, null);
            bufferedImage = op.filter(bufferedImage, null);
            ImageIO.write(bufferedImage, format , new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("图片灰白化异常");
        }
    }



    /**
     * 图片透明化操作(文件物理存盘,使用默认格式)
     * @param imgPath 图片路径
     * @param destPath 图片存放路径
     * @throws Exception 异常
     */
    public static void imageLucency(String imgPath,String destPath)throws Exception{
        try {
            BufferedImage bufferedImage = imageLucency(imgPath);
            ImageIO.write(bufferedImage, imageFormat(imgPath), new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("图片透明化异常");
        }
    }


    /**
     * 图片透明化操作(文件物理存盘,可自定义格式)
     *
     * @param imgPath 图片路径
     * @param format 图片格式
     * @param destPath 图片存放路径
     * @throws Exception 异常
     */
    public static void imageLucency(String imgPath,String format,String destPath)throws Exception{
        try {
            BufferedImage bufferedImage = imageLucency(imgPath);
            ImageIO.write(bufferedImage, format, new File(destPath));
        } catch (Exception e) {
            throw new RuntimeException("图片透明化异常");
        }
    }

    /**
     * 图片透明化操作返回BufferedImage对象
     * @param imgPath 图片路径
     * @return 透明化后的图片对象
     * @throws Exception 异常
     */
    public static BufferedImage imageLucency(String imgPath)throws Exception{
        BufferedImage targetImage = null;
        try {
            //读取图片
            BufferedImage img = ImageIO.read(new FileInputStream(imgPath));
            //透明度
            int alpha = 0;
            //执行透明化
            executeRGB(img, alpha);
            targetImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
            Graphics2D g = targetImage.createGraphics();
            g.drawImage(img, 0, 0, null);
            g.dispose();
        } catch (Exception e) {
            throw new RuntimeException("图片透明化执行异常");
        }
        return targetImage;
    }

    /**
     * 执行透明化的核心算法
     * @param img 图片对象
     * @param alpha 透明度
     * @throws Exception 异常
     */
    public static  void executeRGB(BufferedImage img, int alpha) throws Exception{
        int rgb = 0;//RGB值
        //x表示BufferedImage的x坐标，y表示BufferedImage的y坐标
        for(int x=img.getMinX();x<img.getWidth();x++){
            for(int y=img.getMinY();y<img.getHeight();y++){
                //获取点位的RGB值进行比较重新设定
                rgb = img.getRGB(x, y);
                int R =(rgb & 0xff0000 ) >> 16 ;
                int G= (rgb & 0xff00 ) >> 8 ;
                int B= (rgb & 0xff );
                if(((255-R)<30) && ((255-G)<30) && ((255-B)<30)){
                    rgb = ((alpha + 1) << 24) | (rgb & 0x00ffffff);
                    img.setRGB(x, y, rgb);
                }
            }
        }
    }


    /**
     * 图片格式转化操作(文件物理存盘)
     * @param imgPath 原始图片存放地址
     * @param format  待转换的格式 jpeg,gif,png,bmp等
     * @param destPath 目标文件地址
     * @throws Exception  异常
     */
    public static void formatConvert(String imgPath, String format, String destPath)throws Exception{
        try {
            BufferedImage bufferedImage = ImageIO.read(new File(imgPath));
            ImageIO.write(bufferedImage, format, new File(destPath));
        } catch (IOException e) {
            throw new RuntimeException("文件格式转换出错");
        }
    }



    /**
     * 图片格式转化操作返回BufferedImage对象
     *
     * @param bufferedImag BufferedImage图片转换对象
     * @param format  待转换的格式 jpeg,gif,png,bmp等
     * @param destPath 目标文件地址
     * @throws Exception 异常
     */
    public static void formatConvert(BufferedImage bufferedImag, String format, String destPath)throws Exception{
        try {
            ImageIO.write(bufferedImag, format, new File(destPath));
        } catch (IOException e) {
            throw new RuntimeException("文件格式转换出错");
        }
    }


    /**
     * 获取图片文件的真实格式信息
     * @param imgPath 图片原文件存放地址
     * @return 图片格式
     * @throws Exception 异常
     */
    public static String imageFormat(String imgPath)throws Exception{
        String[] filess = imgPath.split("\\\\");
        String[] formats = filess[filess.length - 1].split("\\.");
        return formats[formats.length - 1];
    }

    /**
     * 图片覆盖（覆盖图压缩到width*height大小，覆盖到底图上）
     * @param base 底图
     * @param imgIn 覆盖图
     * @param x 起始x轴
     * @param y 起始y轴
     * @param width 覆盖宽度
     * @param height 覆盖长度度
     * @return
     * @throws Exception
     */
    public static String coverImage(String base, String imgIn, int x, int y, int width, int height, String codeUrl) throws Exception{
        //原图
        BufferedImage buffImg = ImageIO.read(new FileInputStream(base));
        //覆盖层
        BufferedImage coverImg = ImageIO.read(new URL(imgIn));

        buffImg = coverImage(buffImg, coverImg, x, y, width, height);

        ImageIO.write(buffImg, "png", new File(codeUrl));

        return codeUrl;
    }


    public static String coverImage(String base, BufferedImage imgIn, int x, int y, int width, int height, String codeUrl) throws Exception{
        //原图
        BufferedImage buffImg = ImageIO.read(new FileInputStream(base));
        //覆盖层
        BufferedImage coverImg = imgIn;

        buffImg = coverImage(buffImg, coverImg, x, y, width, height);

        ImageIO.write(buffImg, "png", new File(codeUrl));

        return codeUrl;
    }

    /**
     * 图片覆盖（覆盖图压缩到width*height大小，覆盖到底图上）
     *
     * @param baseBufferedImage 底图
     * @param coverBufferedImage 覆盖图
     * @param x 起始x轴
     * @param y 起始y轴
     * @param width 覆盖宽度
     * @param height 覆盖长度度
     * @return 结果
     * @throws Exception 异常
     */
    public static BufferedImage coverImage(BufferedImage baseBufferedImage, BufferedImage coverBufferedImage, int x, int y, int width, int height) throws Exception{
        // 创建Graphics2D对象，用在底图对象上绘图
        Graphics2D g2d = baseBufferedImage.createGraphics();
        // 绘制
        g2d.drawImage(coverBufferedImage, x, y, width, height, null);
        g2d.dispose();// 释放图形上下文使用的系统资源
        return baseBufferedImage;
    }

    /**
     * 绘制文字
     * @param base
     * @param x
     * @param y
     * @param codeUrl
     * @return
     * @throws Exception
     */
    public static String coverText(String base, int x, int y, String codeUrl, AttributedString as) throws Exception{
        //原图
        BufferedImage buffImg = ImageIO.read(new FileInputStream(base));
        // 创建Graphics2D对象，用在底图对象上绘图
        Graphics2D g2d = buffImg.createGraphics();
        // 绘制
        g2d.drawString(as.getIterator(), x, y);
        g2d.dispose();// 释放图形上下文使用的系统资源
        ImageIO.write(buffImg, "png", new File(codeUrl));

        return codeUrl;
    }

    /**
     * 绘制文字
     * @param base
     * @param text
     * @param x
     * @param y
     * @param codeUrl
     * @return
     * @throws Exception
     */
    public static String coverText(String base, String text, int x, int y, String codeUrl, Font f, Color c) throws Exception{
        //原图
        BufferedImage buffImg = ImageIO.read(new FileInputStream(base));
        // 创建Graphics2D对象，用在底图对象上绘图
        Graphics2D g2d = buffImg.createGraphics();
        g2d.setColor(c);
        g2d.setFont(f);
        // 绘制
        g2d.drawString(text, x, y);
        g2d.dispose();// 释放图形上下文使用的系统资源

        ImageIO.write(buffImg, "png", new File(codeUrl));

        return codeUrl;
    }

    public static InputStream getImage(String url) {
        InputStream inputStream = null;
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                inputStream = connection.getInputStream();
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        return inputStream;
    }


    /**
     * 指定图片宽度和高度和压缩比例对图片进行压缩
     * @param imgsrc   源图片地址
     * @param imgdist  目标图片地址
     * @param widthdist  压缩后图片的宽度
     * @param heightdist 压缩后图片的高度
     * @param rate       压缩的比例
     */
    public static void reduceImg(String imgsrc, String imgdist, Integer widthdist, Integer heightdist, Float rate) {
        try {
            File srcfile = new File(imgsrc);
            // 检查图片文件是否存在
            if (!srcfile.exists()) {
                System.out.println("文件不存在");
            }
            // 如果比例不为空则说明是按比例压缩
            if (rate != null && rate > 0) {
                //获得源图片的宽高存入数组中
                int[] results = getImgWidthHeight(srcfile);
                if (results == null || results[0] == 0 || results[1] == 0) {
                    return;
                } else {
                    //按比例缩放或扩大图片大小，将浮点型转为整型
                    widthdist = (int) (results[0] * rate);
                    heightdist = (int) (results[1] * rate);
                }
            }
            // 开始读取文件并进行压缩
            Image src = ImageIO.read(srcfile);
            // 构造一个类型为预定义图像类型之一的 BufferedImage
            BufferedImage tag = new BufferedImage((int) widthdist, (int) heightdist, BufferedImage.TYPE_INT_RGB);
            //绘制图像  getScaledInstance表示创建此图像的缩放版本，返回一个新的缩放版本Image,按指定的width,height呈现图像
            //Image.SCALE_SMOOTH,选择图像平滑度比缩放速度具有更高优先级的图像缩放算法。
            tag.getGraphics().drawImage(src.getScaledInstance(widthdist, heightdist, Image.SCALE_SMOOTH), 0, 0, null);
            //创建文件输出流
            FileOutputStream out = new FileOutputStream(imgdist);
            //将图片按JPEG压缩，保存到out中
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
            encoder.encode(tag);
            //关闭文件输出流
            out.close();
        } catch (Exception ef) {
            ef.printStackTrace();
        }
    }

    /**
     * 原图大小等比压缩，不改变图片大小，压缩内存
     * @param imgsrc   源图片地址
     * @param imgdist  目标图片地址
     */
    public static void reduceImg(String imgsrc, String imgdist) {
        File srcfile = new File(imgsrc);
        int[] result = getImgWidthHeight(srcfile);
        int width = result[0];
        int height = result[1];
        reduceImg(imgsrc,imgdist,width,height,null);
    }

    /**
     * 原图大小等比压缩
     * @param imgsrc   源图片地址
     * @param imgdist  目标图片地址
     * @param rate  压缩比例
     */
    public static void reduceImg(String imgsrc, String imgdist,Float rate){
        reduceImg(imgsrc,imgdist,null,null,rate);
    }

    /**
     * 原图大小等比压缩
     * @param imgsrc   源图片地址
     * @param imgdist  目标图片地址
     * @param width  指定宽度
     * @param height  指定高度
     */
    public static void reduceImg(String imgsrc, String imgdist,int width,int height){
        reduceImg(imgsrc,imgdist,width,height,null);
    }

    /**
     * 获取图片宽度和高度
     * @param file 文件
     * @return 返回图片的宽度
     */
    public static int[] getImgWidthHeight(File file) {
        InputStream is = null;
        BufferedImage src = null;
        int result[] = { 0, 0 };
        try {
            // 获得文件输入流
            is = new FileInputStream(file);
            // 从流里将图片写入缓冲图片区
            src = ImageIO.read(is);
            // 得到源图片宽
            result[0] =src.getWidth();
            // 得到源图片高
            result[1] =src.getHeight();
        } catch (Exception ef) {
            ef.printStackTrace();
        }finally {
            if(is != null){
                try {
                    is.close();  //关闭输入流
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

}
